# -*- coding: utf-8 -*-
"""
对话管理服务 - 使用 pandas + CSV 存储对话历史

@author: AI Assistant
"""
import json
import pandas as pd
from datetime import datetime
from typing import Dict, Any, List, Optional
from pathlib import Path
from loguru import logger
import uuid

import sys
sys_root = Path(__file__).resolve().parent.parent
if str(sys_root) not in sys.path:
    sys.path.insert(0, str(sys_root))

from config import CONVERSATION_STORAGE_PATH, PARAMETER_STORAGE_PATH
from services.session_service import SessionService
from services.llm_service import LLMService
from services.parameter_service import ParameterService
from utils.prompt_templates import PromptTemplateGenerator
from utils.type_converter import normalize_parameter_changes
from utils.intent_classifier import IntentClassifier


class ConversationManager:
    """对话管理器"""
    
    def __init__(self):
        """初始化对话管理器"""
        self.session_service = SessionService()
        self.llm_service = LLMService()
        self.parameter_service = ParameterService()
        self.prompt_generator = PromptTemplateGenerator()
        self.intent_classifier = IntentClassifier()
    
    def start_conversation(self, session_id: str) -> Optional[Dict[str, Any]]:
        """开始新对话
        
        Args:
            session_id: 会话ID
            
        Returns:
            对话信息字典
        """
        try:
            # 加载会话数据
            session = self.session_service.get_session_by_id(session_id)
            if not session:
                logger.error(f"会话不存在: {session_id}")
                return None
            
            # 生成对话ID
            conversation_id = f"conv_{datetime.now().strftime('%Y%m%d%H%M%S')}_{str(uuid.uuid4())[:8]}"
            
            logger.info(f"开始新对话 - conversation_id: {conversation_id}, session_id: {session_id}")
            
            return {
                "conversation_id": conversation_id,
                "session_id": session_id,
                "created_at": datetime.now().isoformat()
            }
            
        except Exception as e:
            logger.error(f"开始对话失败: {str(e)}", exc_info=True)
            return None
    
    def continue_conversation(
        self,
        session_id: str,
        user_message: str,
        conversation_id: Optional[str] = None
    ) -> Optional[Dict[str, Any]]:
        """继续对话
        
        Args:
            session_id: 会话ID
            user_message: 用户消息
            conversation_id: 对话ID（如果为None则创建新对话）
            
        Returns:
            对话响应字典
        """
        try:
            # 如果没有conversation_id，创建新对话
            if not conversation_id:
                conv_info = self.start_conversation(session_id)
                if not conv_info:
                    return None
                conversation_id = conv_info["conversation_id"]
            
            # 加载会话数据
            session = self.session_service.get_session_by_id(session_id)
            if not session:
                return {"success": False, "message": "会话不存在"}
            
            # ===== 步骤1: 意图识别 =====
            intent_result = self.intent_classifier.predict(user_message, session.para_schema)
            intent_type = intent_result["intent_type"]
            
            logger.info(f"识别意图: {intent_type}, 置信度: {intent_result['confidence']:.2f}")
            
            # 处理未知意图
            if intent_type == IntentClassifier.INTENT_UNKNOWN:
                return {
                    "success": True,
                    "message": "抱歉，我无法理解您的意图。您可以尝试以下方式：\n\n"
                              "1. 参数修改：例如 \"将SA0修改为10\"\n"
                              "2. 降雨假拟：\n"
                              "   - 按比例调整：例如 \"降雨按比例增加到50mm\"\n"
                              "   - 平均分配：例如 \"降雨平均分配到60mm\"",
                    "conversation_id": conversation_id,
                    "understood": False,
                    "intent_type": IntentClassifier.INTENT_UNKNOWN,
                    "parameter_changes": None,
                    "explanation": None,
                    "current_parameters": {"initial_state": session.initial_state, "parameters": session.parameters},
                    "validation_errors": None,
                    "update_result": None,
                    "parameter_ranges": None,
                    "rainfall_simulation": None
                }
            
            # 处理降雨假拟意图
            if intent_type == IntentClassifier.INTENT_RAINFALL:
                # 固定当前降雨量（TODO: 后续从实际数据源获取）
                current_rainfall = 50.0
                
                # 提取降雨假拟信息
                rainfall_info = self.intent_classifier.extract_rainfall_info(user_message, current_rainfall)
                
                logger.info(f"降雨假拟 - 方式: {rainfall_info['method']}, 目标值: {rainfall_info['values']}")
                
                # 格式化数值（保留1位小数）
                formatted_values = [round(v, 1) for v in rainfall_info['values']]
                values_str = ", ".join([f"{v:.1f}" for v in formatted_values])
                
                # 保存对话
                self._save_message(conversation_id, session_id, "user", user_message)
                # 方式名称映射（用于显示）
                method_display = {
                    "proportional": "按比例调整",
                    "average": "平均分配"
                }
                display_method = method_display.get(rainfall_info['method'], rainfall_info['method'])
                response_message = f"好的，我将采用【{display_method}】方式，目标降雨量为 [{values_str}] mm"
                self._save_message(conversation_id, session_id, "assistant", response_message)
                
                # 格式化返回的 rainfall_simulation（保留1位小数）
                formatted_rainfall_info = {
                    "method": rainfall_info['method'],
                    "values": formatted_values
                }
                
                return {
                    "success": True,
                    "message": response_message,
                    "conversation_id": conversation_id,
                    "understood": True,
                    "intent_type": IntentClassifier.INTENT_RAINFALL,
                    "parameter_changes": None,
                    "explanation": f"根据您的要求，使用{display_method}方式进行降雨假拟",
                    "current_parameters": {"initial_state": session.initial_state, "parameters": session.parameters},
                    "validation_errors": None,
                    "update_result": None,
                    "parameter_ranges": None,
                    "rainfall_simulation": formatted_rainfall_info
                }
            
            # ===== 步骤2: 处理参数修改意图（原有流程） =====
            # 生成系统提示词
            system_prompt = self.prompt_generator.generate_system_prompt(
                model_type=session.model_type,
                stcd=session.stcd,
                stcd_name=session.stcd_name or "",
                para_schema=session.para_schema,
                initial_state=session.initial_state,
                parameters=session.parameters
            )
            
            # 加载对话历史
            history = self._load_conversation_history(conversation_id)
            
            # 调用 LLM
            llm_response = self.llm_service.generate_response(
                system_prompt=system_prompt,
                user_message=user_message,
                conversation_history=history
            )
            
            if not llm_response:
                return {"success": False, "message": "LLM 响应失败"}
            
            # 保存对话
            self._save_message(conversation_id, session_id, "user", user_message)
            self._save_message(
                conversation_id, 
                session_id, 
                "assistant", 
                llm_response["message"], 
                llm_response.get("parameter_changes")
            )
            
            # 如果有参数修改，应用并验证
            current_parameters = {"initial_state": session.initial_state, "parameters": session.parameters}
            validation_errors = None
            update_result = None
            filtered_changes = None
            parameter_ranges = None
            explanation = llm_response.get("explanation")  # 初始化 explanation
            
            if llm_response.get("understood") and llm_response.get("parameter_changes"):
                # 标准化参数类型（字符串转数值等）
                normalized_changes = normalize_parameter_changes(
                    llm_response["parameter_changes"],
                    session.para_schema
                )
                
                # 过滤掉超出范围的参数（内部逻辑）
                filtered_changes, filtered_info = self._filter_out_of_range_parameters(
                    normalized_changes,
                    session.para_schema
                )
                
                # 修正 explanation（添加过滤说明）
                explanation = llm_response.get("explanation", "")
                if filtered_info:
                    # 构建过滤说明
                    filter_messages = []
                    for param_path, reason in filtered_info.items():
                        # 提取参数名（去掉路径前缀）
                        param_name = param_path.split(".")[-1]
                        filter_messages.append(f"参数 {param_name} 的{reason}，未被应用")
                    
                    # 添加到 explanation
                    explanation += "\n注意：" + "；".join(filter_messages) + "。"
                
                if not filtered_changes:
                    # 所有参数都被过滤掉了
                    logger.warning("所有参数都超出范围，已被过滤")
                    validation_errors = ["所有参数都超出有效范围"]
                    update_result = {
                        "success": False,
                        "message": "所有参数都超出有效范围",
                        "data": None
                    }
                    parameter_ranges = {}
                else:
                    # 验证参数（使用过滤后的参数）
                    is_valid, errors, warnings = self.parameter_service.validate_parameters(
                        filtered_changes,
                        session.para_schema
                    )
                    
                    if is_valid:
                        # 合并参数（使用过滤后的值）
                        current_parameters = self.parameter_service.merge_parameters(
                            current_parameters,
                            filtered_changes
                        )
                        
                        # 保存参数修改记录（使用过滤后的值）
                        self._save_parameter_changes(
                            session_id,
                            conversation_id,
                            filtered_changes,
                            llm_response.get("explanation")
                        )
                        
                        # 模拟更新成功（不实际调用接口）
                        logger.info(f"参数验证通过，模拟更新成功 - session_id: {session_id}")
                        update_result = {
                            "success": True,
                            "message": "参数验证通过",
                            "data": {
                                "session_id": session_id,
                                "updated_parameters": list(filtered_changes.keys())
                            }
                        }
                    else:
                        validation_errors = errors
                        logger.warning(f"参数验证失败，不调用更新接口")
                        # 设置明确的失败状态
                        error_messages = [err.get("message", str(err)) if isinstance(err, dict) else str(err) for err in errors]
                        update_result = {
                            "success": False,
                            "message": f"参数验证失败: {'; '.join(error_messages)}",
                            "data": None
                        }
                    
                    # 提取参数范围（使用过滤后的参数）
                    parameter_ranges = self._extract_parameter_ranges(filtered_changes, session.para_schema)
            
            return {
                "success": True,
                "message": llm_response["message"],
                "conversation_id": conversation_id,
                "understood": llm_response.get("understood", False),
                "intent_type": IntentClassifier.INTENT_PARAMETER,
                "parameter_changes": filtered_changes if llm_response.get("understood") and llm_response.get("parameter_changes") else llm_response.get("parameter_changes"),
                "explanation": explanation,  # 使用修正后的 explanation
                "current_parameters": current_parameters,
                "validation_errors": validation_errors,
                "update_result": update_result,
                "parameter_ranges": parameter_ranges if llm_response.get("understood") and llm_response.get("parameter_changes") else None,
                "rainfall_simulation": None  # 参数修改意图时为 None
            }
            
        except Exception as e:
            logger.error(f"对话失败: {str(e)}", exc_info=True)
            return {"success": False, "message": f"对话失败: {str(e)}"}
    
    def _filter_out_of_range_parameters(
        self,
        parameter_changes: Dict[str, Any],
        para_schema: Dict[str, Any]
    ) -> tuple:
        """
        过滤掉超出范围的参数（内部逻辑，不对外暴露）
        
        Args:
            parameter_changes: 参数修改字典
            para_schema: 参数模式定义
            
        Returns:
            (filtered_changes, filtered_info)
            - filtered_changes: 过滤后的参数修改字典
            - filtered_info: 被过滤的参数信息 {"param_path": "原因"}
        """
        filtered_changes = {}
        filtered_info = {}  # 记录被过滤的参数信息
        
        for param_path, value in parameter_changes.items():
            if "." in param_path:
                category, param_name = param_path.split(".", 1)
                
                # 获取参数定义
                if category in para_schema and param_name in para_schema[category]:
                    param_def = para_schema[category][param_name]
                    min_val = param_def.get("min")
                    max_val = param_def.get("max")
                    
                    # 检查范围（只有定义了范围才检查）
                    is_valid = True
                    reason = ""
                    
                    if min_val is not None and value < min_val:
                        is_valid = False
                        reason = f"值 {value} 小于最小值 {min_val}"
                        logger.info(f"过滤参数 {param_path}: {reason}")
                    elif max_val is not None and value > max_val:
                        is_valid = False
                        reason = f"值 {value} 超出最大值 {max_val}"
                        logger.info(f"过滤参数 {param_path}: {reason}")
                    
                    if is_valid:
                        filtered_changes[param_path] = value
                    else:
                        # 记录被过滤的参数信息，包含完整范围
                        if min_val is not None and max_val is not None:
                            filtered_info[param_path] = f"值 {value} 超出有效范围 [{min_val}, {max_val}]"
                        elif max_val is not None:
                            filtered_info[param_path] = f"值 {value} 超出最大值 {max_val}"
                        elif min_val is not None:
                            filtered_info[param_path] = f"值 {value} 小于最小值 {min_val}"
                else:
                    # 没有定义，保留
                    filtered_changes[param_path] = value
            else:
                # 格式不正确，保留
                filtered_changes[param_path] = value
        
        return filtered_changes, filtered_info
    
    def _extract_parameter_ranges(
        self,
        parameter_changes: Dict[str, Any],
        para_schema: Dict[str, Any]
    ) -> Dict[str, List[Any]]:
        """
        提取参数的取值范围
        
        Args:
            parameter_changes: 参数修改字典
            para_schema: 参数模式定义
            
        Returns:
            参数范围字典，格式: {"parameter_path": [min, max]}
        """
        ranges = {}
        
        for param_path in parameter_changes.keys():
            # 解析参数路径
            if "." in param_path:
                category, param_name = param_path.split(".", 1)
                
                # 从 schema 中获取范围
                if category in para_schema and param_name in para_schema[category]:
                    param_def = para_schema[category][param_name]
                    min_val = param_def.get("min")
                    max_val = param_def.get("max")
                    
                    # 构建范围
                    if min_val is not None and max_val is not None:
                        ranges[param_path] = [min_val, max_val]
                    elif min_val is not None:
                        ranges[param_path] = [min_val, None]
                    elif max_val is not None:
                        ranges[param_path] = [None, max_val]
                    else:
                        ranges[param_path] = [None, None]
                else:
                    ranges[param_path] = [None, None]
        
        return ranges
    
    def _load_conversation_history(self, conversation_id: str) -> List[Dict[str, str]]:
        """加载对话历史
        
        Args:
            conversation_id: 对话ID
            
        Returns:
            对话历史列表
        """
        try:
            csv_path = CONVERSATION_STORAGE_PATH / f"{conversation_id}.csv"
            if not csv_path.exists():
                return []
            
            df = pd.read_csv(csv_path)
            history = []
            for _, row in df.iterrows():
                history.append({
                    "role": row["role"],
                    "content": row["message"]
                })
            
            return history
            
        except Exception as e:
            logger.error(f"加载对话历史失败: {str(e)}")
            return []
    
    def _save_message(
        self,
        conversation_id: str,
        session_id: str,
        role: str,
        message: str,
        parameter_changes: Optional[Dict[str, Any]] = None
    ):
        """保存消息到 CSV
        
        Args:
            conversation_id: 对话ID
            session_id: 会话ID
            role: 角色（user/assistant）
            message: 消息内容
            parameter_changes: 参数修改（可选）
        """
        try:
            csv_path = CONVERSATION_STORAGE_PATH / f"{conversation_id}.csv"
            
            # 使用 json.dumps 确保格式正确
            param_changes_str = ""
            if parameter_changes:
                param_changes_str = json.dumps(parameter_changes, ensure_ascii=False)
            
            data = {
                "timestamp": [datetime.now().isoformat()],
                "conversation_id": [conversation_id],
                "session_id": [session_id],
                "role": [role],
                "message": [message],
                "parameter_changes": [param_changes_str]
            }
            
            df = pd.DataFrame(data)
            
            # 追加到文件
            if csv_path.exists():
                df.to_csv(csv_path, mode='a', header=False, index=False, encoding='utf-8')
            else:
                df.to_csv(csv_path, index=False, encoding='utf-8')
            
            logger.debug(f"保存消息 - {role}: {message[:50]}...")
            
        except Exception as e:
            logger.error(f"保存消息失败: {str(e)}")
    
    def _save_parameter_changes(
        self,
        session_id: str,
        conversation_id: str,
        parameter_changes: Dict[str, Any],
        reason: Optional[str] = None
    ):
        """保存参数修改记录
        
        Args:
            session_id: 会话ID
            conversation_id: 对话ID
            parameter_changes: 参数修改
            reason: 修改原因
        """
        try:
            csv_path = PARAMETER_STORAGE_PATH / f"{session_id}_changes.csv"
            
            records = []
            for param_path, new_value in parameter_changes.items():
                records.append({
                    "timestamp": datetime.now().isoformat(),
                    "session_id": session_id,
                    "conversation_id": conversation_id,
                    "parameter_path": param_path,
                    "new_value": new_value,
                    "reason": reason or ""
                })
            
            df = pd.DataFrame(records)
            
            # 追加到文件
            if csv_path.exists():
                df.to_csv(csv_path, mode='a', header=False, index=False, encoding='utf-8')
            else:
                df.to_csv(csv_path, index=False, encoding='utf-8')
            
            logger.debug(f"保存参数修改记录 - {len(records)} 条")
            
        except Exception as e:
            logger.error(f"保存参数修改记录失败: {str(e)}")
    
    def get_conversation_history(self, conversation_id: str) -> Optional[List[Dict[str, Any]]]:
        """获取对话历史
        
        Args:
            conversation_id: 对话ID
            
        Returns:
            对话历史列表
        """
        try:
            csv_path = CONVERSATION_STORAGE_PATH / f"{conversation_id}.csv"
            if not csv_path.exists():
                return []
            
            df = pd.read_csv(csv_path)
            history = df.to_dict('records')
            return history
            
        except Exception as e:
            logger.error(f"获取对话历史失败: {str(e)}")
            return None


if __name__ == "__main__":
    manager = ConversationManager()
    print("✅ 对话管理器初始化成功")

